#include <mono/parser/property_node.h>

namespace mono {

const std::string PropertyNode::KNotExists = "not exist";

const std::string PropertyNode::KOutOfRange = "out of range";

PropertyNode::PropertyNode(const std::string & name) 
	: _name(name)
{
	_children.reserve(5);
}


void PropertyNode::addAttribute(const std::string & attribute)
{
	_attributes.push_back(attribute);
}

void PropertyNode::addProperty(const Property & property) 
{
	_properties[property.first] = property.second;
}

void PropertyNode::addNode(const PropertyNodePtr & node) 
{
	_children.push_back(node);
}


size_t PropertyNode::getNumProperties() const 
{
	return _properties.size();
}

size_t PropertyNode::getNumAttributes() const 
{
	return _attributes.size();
}

size_t PropertyNode::getNumNodes() const 
{
	return _children.size();
}


std::string PropertyNode::getName() const 
{
	return _name;
}

bool PropertyNode::getAttribute(size_t id, std::string&value) const 
{
	if(id >= _attributes.size()) {
		return false;
	}
	value = _attributes.at(id);
	return true;
}

bool PropertyNode::getProperty(const std::string & name, const std::string & defaultValue, std::string &value) const 
{
	PropertyMap::const_iterator iter = _properties.find(name);
	if (iter == _properties.end()) {
		value = defaultValue;
	} else {
		value = iter->second;	
	}
	
	return true;
}

bool PropertyNode::getProperty(const std::string & name, std::string &value) const 
{
	PropertyMap::const_iterator iter = _properties.find(name);
	if (iter == _properties.end()){
		return false;
	}
	value = iter->second;
	return 	true;
}

bool PropertyNode::getProperty(size_t id, Property &value) const 
{
	if(id > _properties.size()) {
		return false;
	}
	PropertyMap::const_iterator iter = _properties.begin();
	for(size_t i = 0; i < id; i++) {
		iter ++;
	}
	value = *iter;
	return true;
}

const PropertyNodePtr  PropertyNode::getNode(size_t id) const 
{
	if(id < _children.size()) {
		return _children.at(id);
	} else {
		return PropertyNodePtr();
	}
	
}

const PropertyNodePtr  PropertyNode::getNode(const std::string & name) const 
{
	for (size_t i = 0; i < _children.size(); ++i) {
		if (_children[i]->getName() == name)
			return _children[i];
	}
	
	return PropertyNodePtr();
}

PropertyNodePtr  PropertyNode::getNode(const std::string & name) {
	for (size_t i = 0; i < _children.size(); ++i) {
		if (_children[i]->getName() == name)
			return _children[i];
	}
	
	return PropertyNodePtr();
}

PropertyNodePtr PropertyNode::MergeTrees(const PropertyNodePtr & base, const PropertyNodePtr & sub) 
{
	// merge two trees into one. in case of a name overlap, sub has precedence. a -name- is a node name or a property name
	//    being able to include the attribute list in -name- would be useful!
	
	PropertyNodePtr ret(new PropertyNode(sub->getName()));

	for (size_t i = 0; i < sub->getNumProperties(); ++i) {
		Property p;
		sub->getProperty(i, p);
		ret->addProperty(p);
	}
	std::string attr;
	for (size_t i = 0; i < std::max(sub->getNumAttributes(), base->getNumAttributes()); ++i) {	// TODO: possibility to change this behavior
		if (i < sub->getNumAttributes()){
			attr.clear();
			sub->getAttribute(i, attr);
			ret->addAttribute(attr);
		} else{
			attr.clear();
			base->getAttribute(i, attr);
			ret->addAttribute(attr);
		}
	}
	
	
	for (size_t i = 0; i < sub->getNumNodes(); ++i) {
		const PropertyNodePtr subBranch = sub->getNode(i);
	                                                                         
		const PropertyNodePtr baseBranch = base->getNode(subBranch->getName());	// TODO: possibility to identify with attribute list
		
		if(baseBranch) {
			ret->addNode(MergeTrees(baseBranch, subBranch));
		} else {
			ret->addNode(subBranch);	
		}
		
	}
	
	
	return ret;
}

}
